連假結束啦,我們目前聊天主要都是以對話方式呈現,那為了讓我們的使用者可以同時聊天,也可以傳送可愛的照片,那我們就會需要把照片給傳出去,跟大家一起互相分享!
既然我們都要傳照片了,那我們當然要新增照片的欄位啦
data class Message(
val user_name: String? = null,
val message: String? = null,
val time: Any? = null,
val send_user_id: String? = null,
val send_user_name: String? = null,
val accept_user_id: String? = null,
val accept_user_name: String? = null,
val accept_user_image: String? = null,
val send_user_image: String? = null,
//新增這一行
val image: String? = null
)
然後我們的LastMessage這邊就不做修改囉,我們就仿照Line一般,當今天有新的照片的時候,就直接說傳送一張照片即可~
先在我們的layout新增以下
<ImageButton
android:id="@+id/btn_camera"
android:layout_weight="1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_baseline_photo_camera_24">
</ImageButton>
我們這邊就做的比較簡單一點,我們只一次最多就只能傳送一筆圖片,那如果要傳送多張圖片的話,就把我們的dataClass改成list,就可以啦!
我們首先也要在上面聲明啦
我們把拿到副檔名的funtion在fragment就先叫出來,並且丟入viewmodel
private val resultLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()){ uri ->
if (uri.resultCode == Activity.RESULT_OK){
val selectedUri = uri.data?.data
selectedUri?.let {
Constant.getFileExtension(requireActivity(),it)?.let { it1 ->
chatViewModel.saveImageToFireStorage(
it1,it)
}
}
}
}
這時候我們發現紅字,所以我們就需要來到我們的ChatViewModel,新增啦
這邊我們修改一下寫法,因為之前我們竟然把Activity跟Fragment當成參數傳進去!!,原本想說這樣簡單方便,但是後來經過大大的解說,說有可能因為畫面旋轉,導致fragment的實例是不同的(雖然我們現在在manifest設定不能旋轉),但仍有這種風險,所以我們要把它改成透過livedata的方式,讓fragment來觀察!!
//我們把可下載Uri存在這邊,並且讓fragment來觀察
private val _image = MutableLiveData<Uri>()
val image: LiveData<Uri>
get() = _image
//這邊則是存進去storage失敗的時候,我們也透過livedata的方式存進去資訊
private val _image_state = MutableLiveData<String>()
val image_state: LiveData<String>
get() = _image_state
fun saveImageToFireStorage(type: String, uri: Uri) {
val sdf: StorageReference = FirebaseStorage.getInstance().reference.child(
Constant.CHAT_IMAGE + "_" + System.currentTimeMillis() + "_" + type
)
sdf.putFile(uri)
.addOnSuccessListener { it ->
it.metadata?.reference?.downloadUrl
?.addOnSuccessListener { Uri ->
_image.postValue(Uri)
}
?.addOnFailureListener {
_image_state.postValue(it.toString())
}
}
.addOnFailureListener {
_image_state.postValue(it.toString())
}
}
好的!! 那接下來就是回到fragment來觀察啦!
我們觀察當今天我們觀察到LiveData有資料的時候,我們就要把顯示"已選取一張照片",並且把livedata的值賦予給selectedUri
chatViewModel.image.observe(viewLifecycleOwner, Observer {
if (it != null){
binding.edChatRoomInputMessage.setText("已選取一張照片")
= it.toString()
}
})
★基本上我們不用去管使用者會不會再去選擇其他照片,因為只要他再次選擇其它相簿,他就會重新賦予livedata值,我們只要處理當今天使用者按完送出之後,要把livedata改成null,否則他會一直永遠記得之前的照片,直到關掉App或是換新的照片~
好的,接下來就是我們要傳送訊息啦!
我們只要把在從 DetailFrgament跟不是從DetailFragment傳進來的訊息,都加上image,這樣就好啦!
這邊就先貼chatViewModel.fromDetail.value == true 的message
message = Message(
user_name = accountViewModel.userDetail.value!!.name,
message = binding.edChatRoomInputMessage.text.toString().trim(),
send_user_id = accountViewModel.userDetail.value!!.id,
accept_user_id = matchingViewModel.selectedInvitation.value!!.user_id,
send_user_image = accountViewModel.userDetail.value!!.image,
send_user_name = accountViewModel.userDetail.value!!.name,
time = ServerValue.TIMESTAMP,
accept_user_image = matchingViewModel.selectedInvitation.value?.user_image,
accept_user_name = matchingViewModel.selectedInvitation.value?.user_name,
image = selectedUri
)
好的! 那接下來,我們還需要做一件事,各位夥伴有沒有想法阿!!?
那就是~~
我們需要reset我們的livedata,否則它就會一直存著之前的照片,好的我們直接在viewmodel新增這這個funtion就好啦!
fun resetImage(){
_image.postValue(null)
}
但是.. 這邊有報錯,原本預設是不能賦予livedata null的阿
所以我們這邊要在
gradle的app層級新增以下,這樣就可以啦!
android {
lintOptions {
disable 'NullSafeMutableLiveData'
}
}
# 四、顯示畫面
接下來我們需要來修改我們的item_list_layout,並且還有adapter
我們首先都在我們的message_item_list_me 跟message_item_list_other 這兩個xml裡面新增 imageView,並且預設是不顯示的,這邊就給 message_item_list_me 的範例
<ImageView
android:id="@+id/iv_item_list_me"
android:layout_width="80dp"
android:layout_height="80dp"
android:layout_alignParentEnd="true"
android:visibility="gone"
android:padding="5dp">
</ImageView>
好的,那接下來就回到adapter,我們需要當今天 image這個欄位不是null的時候,我們要把message的view隱藏,並且叫出imageView
所以我們在onBindViewHolder 修改成以下
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
val model = getItem(position)
when(holder){
is MyMessageViewHolder ->{
holder.bind(model)
if(model.image != null){
Constant.loadPetImage(model.image,holder.binding.ivItemListMe)
holder.itemView.iv_item_list_me.visibility = View.VISIBLE
holder.itemView.tv_item_message_me_message.visibility = View.GONE
}
}
is OtherMessageViewHolder -> {
holder.bind(model)
if(model.image != null){
Constant.loadPetImage(model.image,holder.binding.ivItemMessageOtherImage)
holder.itemView.iv_item_list_me.visibility = View.VISIBLE
holder.itemView.item_message_other_message.visibility = View.GONE
}
}
}
}
好的,這樣就大功告成啦!
成果如下,雖然畫面有點醜,之後有空再修!